import set from 'lodash/set';

import { gqcPermissionCheck } from '@kuiper/auth';
import nestedToOneLevelPath from '@kuiper-helpers/nested-to-one-level-path';

import manualTask from './manualTask';
import ignoreWarn from './ignoreWarn';
import unignoreWarn from './unignoreWarn';
import stopTask from './stopTask';
import retryTask from './retryTask';
import TaskRemoveMany from './TaskRemoveMany';
import TaskWarnResolveState from './TaskWarnResolveState';
import escapeRegExp from '../../helpers/escapeRegExp';
import findByIdsEmpty from '../Job/findByIdsEmpty';

export default server => {
	const {
		app: {
			schemaComposer,
			model: { Task },
		},
	} = server;

	server.methods.registerGraphQLSchema({
		name: 'Task',
		model: Task,
		queryFields: {
			Many: 'findMany',
			Pagination: 'pagination',
			Count: 'count',
			One: 'findOne',
		},
		mutationFields: {
			RemoveMany: 'removeMany',
		},
		onPreAddFields(tc) {
			const {
				app: {
					schemaComposer,
					graphqlResolvers,
					schema: { Asset, Job, User, AlertGroup },
				},
			} = server;

			tc.addRelation('job', {
				resolver: () => Job.mongooseResolvers.dataLoader({ lean: true }),
				prepareArgs: {
					_id: source => source.jobId,
				},
				projection: { jobId: 1 },
			});

			tc.addRelation('asset', {
				resolver: () => Asset.mongooseResolvers.dataLoader({ lean: true }),
				prepareArgs: {
					_id: source => source.refId,
				},
				projection: { refId: 1 },
			});

			tc.addRelation('user', {
				resolver: () => User.mongooseResolvers.dataLoader({ lean: true }),
				prepareArgs: {
					_id: source => source.uid,
				},
				projection: { uid: 1 },
			});

			tc.addRelation('alertGroups', {
				resolver: () =>
					AlertGroup.mongooseResolvers
						.dataLoaderMany({ lean: true })
						.wrapResolve(findByIdsEmpty),
				prepareArgs: {
					_ids: source => source.alertSettings.alertGroupIds.filter(Boolean),
				},
				projection: { alertSettings: 1 },
			});

			tc.getFieldTC('result').addRelation('computedStats', {
				prepareArgs: {
					warnings: source => source.warnings || [],
					addition: source => source.addition,
				},
				projection: { warnings: 1, addition: 1 },
				resolver: schemaComposer.createResolver({
					name: 'TaskResultComputedStatsResolver',
					type: 'JSON',
					args: {
						warnings: '[JSON]',
						addition: 'JSON',
					},
					resolve: async ({ args: { warnings, addition } }) => {
						const ret = {};

						if (addition?.respAvg !== undefined) {
							Object.assign(ret, {
								normalNodeCount: 0,
								abnormalNodeCount: 0,
								respAvg: addition?.respAvg,
							});
							addition.detail.forEach(x => {
								if (x.securityStatus === 'warning') {
									ret.abnormalNodeCount += 1;
								} else {
									ret.normalNodeCount += 1;
								}
							});
						}

						if (warnings.length === 0) return ret;

						return warnings.reduce((obj, x) => {
							let dealStatus = 'unresolved';
							if (x.ignoredTime) dealStatus = 'ignored';
							else if (x.isResolved) dealStatus = 'resolved';

							const categoryKey = `category\x01${x.category}`;
							if (!obj[categoryKey]) obj[categoryKey] = 0;
							obj[categoryKey] += 1;
							const categoryStatusKey = `${categoryKey}\x01${dealStatus}`;
							if (!obj[categoryStatusKey]) obj[categoryStatusKey] = 0;
							obj[categoryStatusKey] += 1;

							const levelKey = `level\x01${x.level}`;
							if (!obj[levelKey]) obj[levelKey] = 0;
							obj[levelKey] += 1;
							const levelStatusKey = `${levelKey}\x01${dealStatus}`;
							if (!obj[levelStatusKey]) obj[levelStatusKey] = 0;
							obj[levelStatusKey] += 1;

							return obj;
						}, ret);
					},
				}),
			});

			const ITaskDateRange = schemaComposer.createInputTC({
				name: 'ITaskDateRange',
				fields: {
					startTime: 'Date',
					endTime: 'Date',
				},
			});

			graphqlResolvers.Query.TaskOne = graphqlResolvers.Query.TaskOne.withMiddlewares(
				[
					gqcPermissionCheck({
						required: 'cloud-monitor\x01listTask',
						selfOnlyArgsBuilder(
							_source,
							args,
							{
								request: {
									auth: {
										credentials: { uid },
									},
								},
							}
						) {
							set(args, ['filter', 'uid'], uid);
						},
					}),
				]
			);

			graphqlResolvers.Query.TaskMany = graphqlResolvers.Query.TaskMany.addFilterArg(
				{
					name: 'dateRange',
					type: ITaskDateRange,
					description: 'dateRange.startTime >= x && dateRange.endTime <= x',
					query: (query, value) => {
						if (value.startTime) query.startTime = { $gte: value.startTime };
						if (value.endTime) query.endTime = { $lte: value.endTime };
					},
				}
			)
				.addFilterArg({
					name: 'endTimeGTE',
					type: 'Date',
					description: '结束时间大于等于',
					query: (query, value) => {
						const fieldName = 'endTime';
						let field = query[fieldName] || {};
						if (typeof field !== 'object') field = {};
						query[fieldName] = { ...field, $gte: value };
					},
				})
				.addFilterArg({
					name: 'endTimeLTE',
					type: 'Date',
					description: '结束时间小于等于',
					query: (query, value) => {
						const fieldName = 'endTime';
						let field = query[fieldName] || {};
						if (typeof field !== 'object') field = {};
						query[fieldName] = { ...field, $lte: value };
					},
				})
				.withMiddlewares([
					gqcPermissionCheck({
						required: 'cloud-monitor\x01listTask',
						selfOnlyArgsBuilder(
							_source,
							args,
							{
								request: {
									auth: {
										credentials: { uid },
									},
								},
							}
						) {
							set(args, ['filter', 'uid'], uid);
						},
					}),
				]);

			graphqlResolvers.Query.TaskPagination = graphqlResolvers.Query.TaskPagination.withMiddlewares(
				[
					gqcPermissionCheck({
						required: 'cloud-monitor\x01listTask',
						selfOnlyArgsBuilder(
							_source,
							args,
							{
								request: {
									auth: {
										credentials: { uid },
									},
								},
							}
						) {
							set(args, ['filter', 'uid'], uid);
						},
					}),
				]
			)
				.addFilterArg({
					name: 'targetSearch',
					type: 'String',
					description: '正则过滤: target',
					query: (query, value) => {
						query.target = {
							$regex: escapeRegExp(value.trim()),
							$options: 'i',
						};
					},
				})
				.addFilterArg({
					name: 'startTimeGTE',
					type: 'Date',
					description: '开始时间大于等于',
					query: (query, value) => {
						const fieldName = 'startTime';
						let field = query[fieldName] || {};
						if (typeof field !== 'object') field = {};
						query[fieldName] = { ...field, $gte: value };
					},
				})
				.addFilterArg({
					name: 'startTimeLTE',
					type: 'Date',
					description: '开始时间小于等于',
					query: (query, value) => {
						const fieldName = 'startTime';
						let field = query[fieldName] || {};
						if (typeof field !== 'object') field = {};
						query[fieldName] = { ...field, $lte: value };
					},
				})
				.addFilterArg({
					name: 'endTimeGTE',
					type: 'Date',
					description: '结束时间大于等于',
					query: (query, value) => {
						const fieldName = 'endTime';
						let field = query[fieldName] || {};
						if (typeof field !== 'object') field = {};
						query[fieldName] = { ...field, $gte: value };
					},
				})
				.addFilterArg({
					name: 'endTimeLTE',
					type: 'Date',
					description: '结束时间小于等于',
					query: (query, value) => {
						const fieldName = 'endTime';
						let field = query[fieldName] || {};
						if (typeof field !== 'object') field = {};
						query[fieldName] = { ...field, $lte: value };
					},
				})
				.addFilterArg({
					name: 'dateRange',
					type: ITaskDateRange,
					description: 'dateRange.startTime >= x && dateRange.endTime <= x',
					query: (query, value) => {
						if (value.startTime) query.startTime = { $gte: value.startTime };
						if (value.endTime) query.endTime = { $lte: value.endTime };
					},
				})
				.addFilterArg({
					name: 'statusIn',
					type: '[String]',
					description: 'status: { $in: [] }',
					query: (query, value) => {
						query.status = { $in: value };
					},
				})
				.addFilterArg({
					name: 'resultSecurityStatus',
					type: 'String',
					description: 'result.addition.detail.securityStatus',
					query: (query, value) => {
						query['result.addition.detail.securityStatus'] = value;
					},
				})
				.addFilterArg({
					name: 'taskSettingsCollectArea',
					type: 'String',
					description: 'taskSettingsCollectArea',
					query: (query, value) => {
						query['taskSettings.collect.area'] = value;
					},
				})
				.addFilterArg({
					name: 'taskSettings',
					type: 'TaskTaskSettingsInput',
					description: 'target',
					query: (query, value) => {
						Object.assign(query, nestedToOneLevelPath(value, 'taskSettings'));
					},
				})
				.addFilterArg({
					name: 'result',
					type: 'TaskResultInput',
					description: 'target',
					query: (query, value) => {
						Object.assign(query, nestedToOneLevelPath(value, 'result'));
					},
				})
				.addFilterArg({
					name: 'activeOrRecentUpdate',
					type: 'Date',
					description: 'still active or recent update',
					query: (query, value) => {
						query.$or = (query.$or || []).concat(
							{ status: { $in: ['waiting', 'active'] } },
							{ endTime: { $gte: value } }
						);
					},
				})
				.addFilterArg({
					name: 'vulTriggerType',
					type: 'String',
					description:
						'脆弱性监测方式：系统增量(schedule-increment)、系统全量(schedule-full)、自定义(manual)、专项(special)',
					query: (query, value) => {
						switch (value) {
							case 'schedule-increment':
								query.triggerType = 'scheduleIncrement';
								break;
							case 'special':
								query.triggerType = 'special';
								break;
							case 'manual':
								query.triggerType = 'manual';
								break;
							default:
								query.triggerType = 'schedule';
						}
					},
				});

			graphqlResolvers.Mutation.TaskRemoveMany = schemaComposer
				.createResolver(TaskRemoveMany)
				.withMiddlewares([
					gqcPermissionCheck({
						required: 'cloud-monitor\x01removeTask',
						selfOnlyArgsBuilder(
							_source,
							args,
							{
								request: {
									auth: {
										credentials: { uid },
									},
								},
							}
						) {
							set(args, ['filter', 'uid'], uid);
						},
					}),
				]);
		},
	});

	schemaComposer.Mutation.addFields({
		stopTask,
		retryTask,
		ignoreWarn,
		unignoreWarn,
		manualTask,
		TaskWarnResolveState,
	});
};
